﻿namespace MinesweeperGame
{
    using System;

    public class Minesweeper
    {
        private const string Restart = "restart";
        private const string Top = "top";
        private const string Exit = "exit";
        private const string Help = "help";
        private const string HelpQuestionMark = "?";

        private int mineFieldRows;
        private int mineFieldCols;
        private int minesCount;

        private GameEngine engine;
        private Highscore highscore;

        public Minesweeper() : this(5, 10, 15)
        {
        }

        public Minesweeper(int mineFieldRows, int mineFieldCols, int minesCount)
        {
            if (mineFieldRows < 1 || mineFieldCols < 1)
            {
                throw new ArgumentOutOfRangeException("Rows and cols must be positive numbers.");
            }

            if (minesCount < 0 || mineFieldRows * mineFieldCols < minesCount)
            {
                throw new ArgumentOutOfRangeException("Mines count must be in range 0 - " + mineFieldRows * mineFieldCols + ".");
            }

            this.mineFieldRows = mineFieldRows;
            this.mineFieldCols = mineFieldCols;
            this.minesCount = minesCount;

            this.engine = new GameEngine(this.mineFieldRows, this.mineFieldCols, this.minesCount);
            int numberOfSavedHighscores = 5;
            this.highscore = new Highscore(numberOfSavedHighscores);
        }

        public static void Main(string[] args)
        {
            Minesweeper game;
            if (args.Length == 0)
            {
                game = new Minesweeper();
            }
            else
            {
                try
                {
                    int rows = Convert.ToInt32(args[0]);
                    int cols = Convert.ToInt32(args[1]);
                    int minesCount = Convert.ToInt32(args[2]);
                    game = new Minesweeper(rows, cols, minesCount);
                }
                catch (FormatException)
                {
                    Console.WriteLine("Invalid arguments! Format is: rows cols mines (i.e. minesweeper 5 10 15).");
                    return;
                }
                catch (Exception)
                {
                    Console.WriteLine("Invalid arguments! Format is: rows cols mines (i.e. minesweeper 5 10 15).");
                    return;
                }
            }

            game.StartNewGame();

            bool gameRunning = true;
            while (gameRunning)
            {
                Console.Write("\nEnter command or row and column: ");
                string input = Console.ReadLine().Trim();
                string[] command = input.Split(' ');

                if (command.Length == 2)
                {
                    game.OpenCell(command[0], command[1]);
                }
                else if (command.Length == 1)
                {
                    switch (command[0])
                    {
                        case Restart:
                            game.engine.StartNewGame();
                            break;
                        case Top:
                            Console.WriteLine(game.highscore.DisplayHighscores());
                            break;
                        case Exit:
                            gameRunning = false;
                            break;
                        case Help:
                        case HelpQuestionMark:
                            game.DisplayHelpMsg();
                            break;
                        default:
                            Console.WriteLine("No such command!");
                            break;
                    }
                }
                else
                {
                    Console.WriteLine("Invalid input!");
                }
            }

            Console.WriteLine("Good Bye");
        }

        private void StartNewGame()
        {
            Console.Write("Welcome to the game “Minesweeper”! ");
            Console.WriteLine("Try to reveal all cells without mines.\n");
            this.DisplayHelpMsg();
            Console.WriteLine();

            this.engine.StartNewGame();
        }

        private void DisplayHelpMsg()
        {
            Console.WriteLine("Avaliable commands:");
            Console.WriteLine(Top + " - to view the scoreboard;");
            Console.WriteLine(Restart + " - to start a new game;");
            Console.WriteLine(Help + " or " + HelpQuestionMark + " - to display avaliable commands;");
            Console.WriteLine(Exit + " - to quit the game.");
        }

        private void OpenCell(string inputRow, string inputCol)
        {
            int row;
            int col;
            try
            {
                row = Convert.ToInt32(inputRow);
                col = Convert.ToInt32(inputCol);
                bool rowValid = 0 <= row && row < this.mineFieldRows;
                bool colValid = 0 <= col && col < this.mineFieldCols;
                if (rowValid && colValid)
                {
                    this.PlayTurn(row, col);
                }
                else
                {
                    Console.WriteLine("Row and col input is not valid!");
                }
            }
            catch (FormatException)
            {
                Console.WriteLine("Invalid format for row and col input!");
            }
            catch (OverflowException)
            {
                Console.WriteLine("Row or col can not be such a big number!");
            }
            catch (Exception ex)
            {
                throw new Exception("Unexpected exception!", ex);
            }
        }

        private void PlayTurn(int row, int col)
        {
            if (this.engine.ValidateCell(row, col))
            {
                this.engine.OpenCell(row, col);
                bool openedMine = this.engine.CheckCellForMine(row, col);
                if (openedMine)
                {
                    this.engine.OpenAllCells();
                    this.engine.RenderGame();
                    Console.WriteLine("Booooom! You were killed by a mine.");

                    // Substitute -1, because last opened cell is mine.
                    int openCellsCount = this.engine.CountOpenCells() - 1;
                    string cell = (openCellsCount < 2) ? "cell" : "cells";
                    Console.WriteLine("You revealed {0} {1} without mines.", openCellsCount, cell);

                    bool newHighscore = this.highscore.NewHighscore(openCellsCount);
                    if (newHighscore)
                    {
                        string playerName = GetPlayerName();
                        this.highscore.SaveHighscore(openCellsCount, playerName);
                    }

                    Console.WriteLine(this.highscore.DisplayHighscores());
                    this.StartNewGame();
                }
                else
                {
                    this.engine.RenderGame();
                }
            }
            else
            {
                Console.WriteLine("This cell is already opened!");
            }
        }

        static private string GetPlayerName()
        {
            string playerName;
            do
            {
                Console.Write("Please enter your name for the top scoreboard: ");
                playerName = Console.ReadLine();
            }
            while (string.IsNullOrEmpty(playerName));

            return playerName;
        }
    }
}
